package com.huawei.codearts.tools.aop;

import com.huawei.codearts.tools.exception.CustomException;
import com.huawei.codearts.tools.utils.HeaderUtils;
import jakarta.persistence.Entity;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.DigestUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.security.MessageDigest;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 接口验签 AOP
 */

@Slf4j
@Aspect
@Component
public class SignAspect {

    @Value("${api-entity-package}")
    private String bathApiEntityPackagePath;

    @Value("${api-appSecret}")
    private String appSecret;

    @Pointcut("execution(* com.huawei.codearts.controller.*.*(..))")
    public void beforePointcut() {}

    @Pointcut("execution(public * com.huawei.codearts.controller.TestController.*(..))")
    public void verify() {}

    @Pointcut("execution(public * com.huawei.codearts.controller.Test*.*(..))"+
            "&& !execution(public * com.huawei.codearts.controller.TestController.*(..))")
    public void test() {}  /** 开发阶段使用test 方便测试 */

    @Before("test()")  // 前置通知
    public void doBefore(JoinPoint joinPoint) throws Throwable {
        // 获取信息头里的sign
//        String sign = HeaderUtils.get("sign");
        // 获取多个key值 sign、token、traceId
        Map<String, String> hmap = HeaderUtils.get("token", "sign", "traceid", "timestamp");
        String timestamp = hmap.get("timestamp");
        String sign = hmap.get("sign");
        String token = hmap.get("token");
        String traceId = hmap.get("traceid");
        log.info("SignAspect header signature... {}, token... {}, traceId... {}, timestamp... {}", sign, token, traceId, timestamp);

        // 获取方法请求参数
        Object[] objs = joinPoint.getArgs();
        // 参数名
        String[] argNames = ((MethodSignature) joinPoint.getSignature()).getParameterNames();
        Map<String, Object> paramMap = new HashMap<String, Object>();
        int i = 0;
        for (Object obj : objs) {
            System.out.println("obj..."+obj);
            boolean p = false;
            // 是否是实体
            Class<?> clazz = obj.getClass();
            if (this.isEntityClass(clazz)) {
                System.out.println(clazz.getName() + " is an entity class.");
                Field[] fields = clazz.getDeclaredFields();
                for (Field field : fields) {
                    field.setAccessible(true); // 使得私有属性也可以访问
                    Object value = field.get(obj);
                    if (Objects.isNull(value) || value.equals("") || value.equals(0) || value.equals(0L)) {
                        continue;
                    }
                    paramMap.put(field.getName(), value);
                }
                p = true;
            }
            // 是否是map
            if (obj instanceof Map) {
                System.out.println(clazz.getName() + " is an map.");
                Map<String, Object> map = (Map) obj;
                for (Map.Entry<String, Object> entry : map.entrySet()) {
                    System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
                    paramMap.put(entry.getKey(), entry.getValue());
                }
                p = true;
            }
            // 其他类型
            if (!p) {
                System.out.println(clazz.getName() + " is not an entity class or not an map.");
                paramMap.put(argNames[i], obj);
            }
            i++;
        }
        // 时间戳
        if (Objects.isNull(paramMap.get("timestamp")) || paramMap.get("timestamp").equals("")) {
            paramMap.put("timestamp", Optional.ofNullable(timestamp).map(Object::toString).orElse(""));
        }
        log.info("SignAspect paramMap: {}", paramMap);

        // 清除值为空字符串("")、null或0的键值对
//        Iterator<Map.Entry<String, Object>> iterator = paramMap.entrySet().iterator();
//        while (iterator.hasNext()) {
//            Map.Entry<String, Object> entry = iterator.next();
//            if (Objects.isNull(entry.getValue()) || entry.getValue().equals("") || entry.getValue().equals(0)) {
//                iterator.remove();
//            }
//        }

        // 移除signature，签名不参与排序加密
        Object signature = paramMap.remove("signature");
        if (Objects.isNull(signature) || signature.equals("")) {
            signature = Optional.ofNullable(sign).map(Object::toString).orElse("");
        }

        //校验时间戳
        checkSignTime(paramMap.get("timestamp").toString());

        System.out.println("signature..."+signature);
        if (!signature.equals(sign(paramMap, appSecret))) {
            throw new RuntimeException("signature verification failed");
        }

    }

    /**
     * 生成签名
     * @param paramMap 参数列表
     * @param appSecret 密钥
     * @return 签名字符串
     */
    private String sign(Map<String, Object> paramMap, String appSecret) {
        List<String> paramList = new ArrayList<String>(paramMap.keySet());
        System.out.println("paramList..."+paramList);
        Collections.sort(paramList);  // 自然排序 升序
        StringBuffer signsb = new StringBuffer();
        for (String str : paramList) {
            if (paramMap.get(str) == null || paramMap.get(str).equals("")) {
                continue;
            }
            signsb.append("&").append(str).append("=").append(paramMap.get(str));
        }
        signsb.append("&appSecret=").append(appSecret);
        String signStr = DigestUtils.md5DigestAsHex(signsb.substring(1).getBytes()).toUpperCase();
        System.out.println("signStr..."+signStr);
        return signStr;
    }

    /**
     * 时间戳校验
     * @param timestamp 时间戳 前后10分钟 (毫秒)
     * @throws Throwable
     */
    private void checkSignTime(String timestamp) throws Throwable {
        long currentTime = System.currentTimeMillis();
        long requestTime = 0;
        try {
            requestTime = Long.parseLong(timestamp);
        } catch (NumberFormatException e) {
            throw new CustomException("未找到时间戳");
        }
        if (Math.abs(currentTime - requestTime) > 600000) {
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            throw new CustomException(String.format("请求已过期，服务器当前时间:%s", simpleDateFormat.format(currentTime)));
        }
    }

    /**
     * 实体是否有属性字段
     */
    public static boolean hasFields(Class<?> clazz) {
        Field[] fields = clazz.getDeclaredFields();
        return fields.length > 0;
    }

    /**
     * 检查是否是自定义实体model类
     */
    public boolean isEntityClass(Class<?> clazz) {
        if (!hasFields(clazz)) {
            return false;
        }

        Annotation[] annotations = clazz.getAnnotations();
        for (Annotation annotation : annotations) {
            if (annotation.annotationType() == Entity.class) {
                return false;
            }
        }

        if (!clazz.getPackageName().equals(this.bathApiEntityPackagePath)) {
            return false;
        }

        return true;
    }

}